using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using static TMPro.SpriteAssetUtilities.TexturePacker_JsonArray;

public enum UILayer
{
    Bottom,
    Middle,
    Top,
    TopMost,
}

public interface IFrame
{
    int Id { get; set; }
    void OnCreate(GameObject gameObject,YooAsset.AssetHandle handle);
    void OnDestroy();
}

public class UIFrameManager : MonoBehaviour
{
    [SerializeField]
    Transform rootTransform;
    public static UIFrameManager Instance { get;private set; }

    Dictionary<UILayer, Transform> name2Layers;
    private void Awake()
    {
        Instance = this;

        name2Layers = new Dictionary<UILayer, Transform>(32);
        for(int i = 0;i< rootTransform.childCount;++i)
        {
            name2Layers[(UILayer)i] = rootTransform.GetChild(i).transform;
        }
        mFrames = new Dictionary<long, IFrame>(1024);
    }

    private void OnApplicationQuit()
    {
        List<IFrame> frames = new List<IFrame>(mFrames.Count);
        foreach (var frame in mFrames.Values)
        {
            frames.Add(frame);
        }
        foreach (var frame in frames)
        {
            CloseFrame(frame);
        }
        mFrames.Clear();
    }

    Dictionary<long, IFrame> mFrames;

    public void OpenFrame<T>(int frameId = 1,UILayer layer = UILayer.Middle,string packageName = "DefaultPackage",string assetName = "") where T : class,IFrame,new()
    {
        OpenFrame(typeof(T),frameId,layer,packageName,assetName);
    }

    public void SetParent(Transform child,UILayer layer = UILayer.Top)
    {
        if (null != child)
        {
            name2Layers.TryGetValue(layer, out var parent);
            child.SetParent(parent, false);
        }
    }

    public void OpenFrame(System.Type type,int frameId = 1, UILayer layer = UILayer.Middle, string packageName = "DefaultPackage", string assetName = "")
    {
        if (!name2Layers.TryGetValue(layer, out var parent))
        {
            Debug.LogError($"Layer Error {layer}");
            return;
        }

        int frameTypeId = type.GetHashCode();
        long guid = frameTypeId << 32 | frameId;
        if (mFrames.ContainsKey(guid))
        {
            Debug.LogError($"Frame Of {type.Name} Already Existed");
            return;
        }

        var package = YooAsset.YooAssets.GetPackage(packageName);
        if (null == package)
        {
            Debug.LogError($"OpenFrame Failed package ={packageName} Not Found");
            return;
        }

        if (string.IsNullOrEmpty(assetName))
        {
            assetName = type.Name;
        }

        var frameHandle = package.LoadAssetSync<GameObject>($"{assetName}");
        if (null == frameHandle)
        {
            Debug.LogError($"OpenFrame Failed Load {assetName}.prefab Failed");
            return;
        }

        if (frameHandle.AssetObject is not GameObject frameGOTemplate)
        {
            frameHandle.Dispose();
            Debug.LogError($"OpenFrame Failed {assetName}.prefab Is Not Gameobject");
            return;
        }

        var frameGO = UnityEngine.Object.Instantiate(frameGOTemplate) as GameObject;
        frameGO.transform.SetParent(parent,false);

        IFrame frame = type.Assembly.CreateInstance(type.FullName) as IFrame;
        frame.Id = frameId;
        mFrames.Add(guid, frame);

        try
        {
            frame.OnCreate(frameGO, frameHandle);
        }
        catch (System.Exception e)
        {
            Debug.LogError($"{type.Name} OnCreate =>{e}");
        }
    }

    void CloseFrame(long guid)
    {
        if (mFrames.TryGetValue(guid, out IFrame frame))
        {
            mFrames.Remove(guid);

            try
            {
                frame.OnDestroy();
            }
            catch (System.Exception e)
            {
                Debug.LogError($"{frame.GetType().Name} OnCreate =>{e}");
            }
        }
    }

    public void CloseFrame<T>(T frame) where T : class, IFrame
    {
        int frameTypeId = frame.GetType().GetHashCode();
        long guid = frameTypeId << 32 | frame.Id;
        CloseFrame(guid);
    }

    public void CloseFrame<T>(int frameId=1) where T : class, IFrame
    {
        int frameTypeId = typeof(T).GetHashCode();
        long guid = frameTypeId << 32 | frameId;
        CloseFrame(guid);
    }
}
